#include <windows.h>
#include <extend.h>

#define FP_OFF(x) ((unsigned)(x))

void DebugN( LPSTR, LONG );

#define MET_METHOD    0
#define MET_DATA      1
#define MET_CLASSDATA 2
#define MET_INLINE    3
#define MET_VIRTUAL   4

typedef struct
{
   PDYNSYM pMessage;
   LPVOID  pFunction;
   WORD    wData;
   WORD    wScope;
   WORD    wInitValue;
} METHOD, * PMETHOD;

typedef struct
{
   LPSTR   szName;
   WORD    wDatas;
   PMETHOD pMethods;
   WORD    wMethods;
   WORD    wHashKey;
   CLIPVAR aClassDatas;  // Array for ClassDatas
   CLIPVAR aInlines;     // Array for inline codeblocks
   CLIPVAR aInitValues;  // Array for Datas init values
} CLASS, * PCLASS;

#define BUCKET 4

void Debug( LPSTR );
void _Eval( PCLIPVAR );

extern PCLIPVAR _eval, _lbase;
extern PDYNSYM _symEval;

POVM   hClasses = 0;
PCLASS pClasses = 0;
WORD   wClasses = 0;

PDYNSYM msgClassName = 0, msgClassH = 0, msgEval = 0;
PMETHOD pMethod = 0;

//----------------------------------------------------------------------------//

CLIPPER __CLASSNEW() // cClassName, nDatas --> hClass
{
   if( ! pClasses )
      hClasses = _vmAlloc( sizeof( CLASS ) );
   else
      _vmRealloc( hClasses, _vmSize( hClasses ) + sizeof( CLASS ) );
   pClasses = _vmLock( hClasses );

   pClasses[ wClasses ].szName   = _vmLock( _vmAlloc( _parclen( 1 ) + 1 ) );
   _strcpy( pClasses[ wClasses ].szName, _parc( 1 ) );

   pClasses[ wClasses ].wDatas   = _parni( 2 );
   pClasses[ wClasses ].pMethods = _vmLock( _vmAlloc( 100 * sizeof( METHOD ) ) );
   pClasses[ wClasses ].wHashKey = 25; // BUCKET = 4 repetitions
   pClasses[ wClasses ].wMethods = 0;

   _Array( &pClasses[ wClasses ].aClassDatas, 0 );
   _Array( &pClasses[ wClasses ].aInlines,    0 );

   _bset( pClasses[ wClasses ].pMethods, 0, 100 * sizeof( METHOD ) );

   _retni( ++wClasses );
}

//----------------------------------------------------------------------------//

CLIPPER __CLASSNAM() // hClass --> cClassName
{
   WORD wClass = _parni( 1 );

   if( wClass <= wClasses )
      _retc( pClasses[ wClass - 1 ].szName );
   else
      _retc( "" );
}

//----------------------------------------------------------------------------//

CLIPPER __CLASSINS() // hClass --> oNewObject
{
   WORD wClass = _parni( 1 );

   if( wClass <= wClasses )
   {
      _Array( _eval, pClasses[ wClass - 1 ].wDatas );
      _eval->wType  = VT_OBJECT;
      _eval->wClass = wClass;
   }
   else
      _ret();
}

//----------------------------------------------------------------------------//

static DictRealloc( PCLASS pClass )
{
}

//----------------------------------------------------------------------------//

static CLIPPER GetData( void )
{
   _AGet( _lbase + 1, pMethod->wData, 0xFFFF, _eval );
}

//----------------------------------------------------------------------------//

static CLIPPER SetData( void )
{
   _ASet( _lbase + 1, pMethod->wData, _lbase + 2 );
}

//----------------------------------------------------------------------------//

static CLIPPER GetClassData( void )
{
   WORD wClass = ( _lbase + 1 )->wClass;

   if( wClass && wClass <= wClasses )
      _AGet( &pClasses[ wClass - 1 ].aClassDatas, pMethod->wData, 0xFFFF, _eval );
}

//----------------------------------------------------------------------------//

static CLIPPER SetClassData( void )
{
   WORD wClass = ( _lbase + 1 )->wClass;

   if( wClass && wClass <= wClasses )
      _ASet( &pClasses[ wClass - 1 ].aClassDatas, pMethod->wData, _lbase + 2 );
}

//----------------------------------------------------------------------------//

static CLIPPER EvalInline( void )
{
   CLIPVAR block;
   WORD wClass = ( _lbase + 1 )->wClass;
   WORD w;

   _AGet( &pClasses[ wClass - 1 ].aInlines, pMethod->wData, VT_BLOCK, &block );

   _PutDynSym( _symEval );
   _Push( &block );
   for( w = 1; w <= _pcount(); w++ )
      _Push( _param( w, 0xFFFF ) );
   _do( _pcount() );
}

//----------------------------------------------------------------------------//

static CLIPPER Virtual( void )
{
   _PutNil();
}

//----------------------------------------------------------------------------//

CLIPPER __CLASSADD() // hClass, cMessage, pFunction, nType
{
   WORD wClass = _parnl( 1 );
   WORD wType  = _parni( 4 );
   PCLASS pClass;
   PDYNSYM pMessage;
   WORD wAt, wLimit;

   DebugN( "wType", wType );

   if( wClass && wClass <= wClasses )
   {
      pClass   = &pClasses[ wClass - 1 ];
      pMessage = _GetDynSym( _parc( 2 ) );
      wAt      = ( FP_OFF( pMessage ) % pClass->wHashKey ) * BUCKET;
      wLimit   = wAt + BUCKET;

      if( pClass->wMethods > ( pClass->wHashKey * BUCKET * 2/3 ) )
         DictRealloc( pClass );

      while( ( wAt < wLimit ) &&
             ( pClass->pMethods[ wAt ].pMessage &&
             ( pClass->pMethods[ wAt ].pMessage != pMessage ) ) )
         wAt++;

      if( wAt <= wLimit )
      {
         pClass->pMethods[ wAt ].pMessage = pMessage;
         switch( wType )
         {
            case MET_METHOD:
                 pClass->pMethods[ wAt ].pFunction = ( LPVOID ) _parnl( 3 );
                 break;

            case MET_DATA:
                 pClass->pMethods[ wAt ].wData = _parnl( 3 );
                 if( pMessage->pSymbol->szName[ 0 ] == '_' )
                    pClass->pMethods[ wAt ].pFunction = SetData;
                 else
                    pClass->pMethods[ wAt ].pFunction = GetData;
                 break;

            case MET_CLASSDATA:
                 pClass->pMethods[ wAt ].wData = _parnl( 3 );
                 if( _ALen( &pClass->aClassDatas ) < ( LONG ) _parnl( 3 ) )
                    _ASize( &pClass->aClassDatas, _parnl( 3 ) );

                 if( pMessage->pSymbol->szName[ 0 ] == '_' )
                    pClass->pMethods[ wAt ].pFunction = SetClassData;
                 else
                    pClass->pMethods[ wAt ].pFunction = GetClassData;
                 break;

            case MET_INLINE:
                 pClass->pMethods[ wAt ].wData = _ALen( &pClass->aInlines ) + 1;
                 _ASize( &pClass->aInlines, pClass->pMethods[ wAt ].wData );
                 _ASet( &pClass->aInlines, pClass->pMethods[ wAt ].wData,
                        _param( 3, VT_BLOCK ) );
                 pClass->pMethods[ wAt ].pFunction = EvalInline;
                 break;

            case MET_VIRTUAL:
                 pClass->pMethods[ wAt ].pFunction = Virtual;
                 break;

            default:
                 MsgAlert( "Invalid method type at __ClassAdd", "Error" );
                 break;
         }
         pClass->wMethods++;
         DebugN( "Added method", wAt );
         return;
      }
   }
}

//----------------------------------------------------------------------------//

static CLIPPER ClassH( void )
{
   _retni( ( _lbase + 1 )->wClass );
}

//----------------------------------------------------------------------------//

static CLIPPER ClassName( void )
{
   WORD wClass = IF( ( _lbase + 1 )->wType == VT_ARRAY,
                     ( _lbase + 1 )->wClass, 0 );

   // Variables by reference
   if( ( ! wClass ) && ( _lbase + 1 )->wType == VT_VREF )
      if( ( ( PCLIPVAR ) ( _lbase + 1 )->pVoid )->wType == VT_ARRAY )
         wClass = ( ( PCLIPVAR ) ( _lbase + 1 )->pVoid )->wClass;

   if( wClass && ( wClass <= wClasses ) )
      _retc( pClasses[ wClass - 1 ].szName );
   else
   {
      switch( ( _lbase + 1 )->wType )
      {
         case VT_ARRAY:
              _retc( "ARRAY" );
              break;

         case VT_BLOCK:
              _retc( "BLOCK" );
              break;

         case VT_CHARACTER:
              _retc( "CHARACTER" );
              break;

         case VT_DATE:
              _retc( "DATE" );
              break;

         case VT_LOGICAL:
              _retc( "LOGICAL" );
              break;

         case VT_FLOAT:
         case VT_NUMERIC:
              _retc( "NUMERIC" );
              break;

         default:
              _retc( "NIL" );
              break;
      }
   }
}

//----------------------------------------------------------------------------//

static CLIPPER Eval( void )
{
}

//----------------------------------------------------------------------------//

LPVOID GetMethod( WORD wClass, PDYNSYM pMessage )
{
   PCLASS pClass;
   WORD   wAt, wLimit;

   if( ! msgClassName )
   {
      msgClassName = _GetDynSym( "CLASSNAME" );
      msgClassH    = _GetDynSym( "CLASSH" );
      msgEval      = _GetDynSym( "EVAL" );
   }

   if( wClass && wClass <= wClasses )
   {
      pClass = &pClasses[ wClass - 1 ];
      wAt    = ( FP_OFF( pMessage ) % pClass->wHashKey ) * BUCKET;
      wLimit = wAt + BUCKET;

      DebugN( "wAt", wAt );

      pMethod = 0;

      while( wAt < wLimit )
      {
         if( pClass->pMethods[ wAt ].pMessage == pMessage )
         {
            pMethod = &pClass->pMethods[ wAt ];
            return pClass->pMethods[ wAt ].pFunction;
         }
         wAt++;
      }
   }

   if( pMessage == msgClassName )
      return ClassName;

   else if( pMessage == msgClassH )
      return ClassH;

   else if( pMessage == msgEval )
      return Eval;

   return 0;
}

//----------------------------------------------------------------------------//
